import os
import PySpin
import sys

NUM_IMAGES = 10 #要抓取的图片数量

def acquire_images(cam, nodemap, nodemap_tldevice):
    '''
    从设备中获取图片
    :param cam: 从相机中获取图片
    :param nodemap:设备的节点映射
    :param nodemap_tldevice:传输层设备节点映射。
    :return:True代表成功
    '''
    print('***** 图像获取 *****')
    try:
        result = True

        #为了访问节点条目，必须将它们转换为指针类型(这里是CEnumerationPtr)
        node_acquisition_mode = PySpin.CEnumerationPtr(nodemap.GetNode('AcquisitionMode'))
        if not PySpin.IsAvailable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
            print('无法将采集模式设置为连续(enum检索)。流产……') #无法将获取模式设置为连续，异常终止
            return False

        #从枚举节点检索入口节点
        node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName('Continuous')
        if not PySpin.IsAvailable(node_acquisition_mode_continuous) or not PySpin.IsReadable(node_acquisition_mode_continuous):
            print('无法将采集模式设置为连续(条目检索)。流产……')
            return False
        #从入口节点检索整数值
        acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()
        # 设置条目节点的整数值为枚举节点的新值
        node_acquisition_mode.SetIntValue(acquisition_mode_continuous)

        print('获取模式设置为了连续（continuous）')

        #下面终于到了获取图像啦！
        #注意，相机的获取图像以来获取模式，Single Frame单帧只能单张图片；multi frame多帧，一个图片集多张图片；连续模式获取连续的图像流
        #当没有图片需要时，需要将图片获取结束
        cam.BeginAcquisition()
        print('终于开始获取图片了')

        #****检索设备号，可有可无，方便定义文件名而已
        device_serial_number = ''
        node_device_serial_number = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceSerialNumber'))
        if PySpin.IsAvailable(node_device_serial_number) and PySpin.IsReadable(node_device_serial_number):
            device_serial_number = node_device_serial_number.GetValue()
            print('设备序列号为%s...' %device_serial_number)

        #检索，转化，存储图片集
        for i in range(2):
            try:
                #注意
                #捕捉的图片在相机的缓存区，尝试捕获不使相机中止的图片
                #之后
                #一旦缓存中的图像被保存并且/或不再需要，图像必须被释放，以保持缓冲区从填充。
                image_result = cam.GetNextImage(1000)

                #确保图片是完整的
                #注意
                #图片必须是易于检测完整性的。
                if image_result.IsIncomplete():
                    print('图片是不完整的，现在图片的状态是%d..' %image_result.GetImageStatus())
                else:
                    width = image_result.GetWidth()
                    height = image_result.GetHeight()
                    print('抓取的图片%d，宽：%d，高：%d'%(i, width, height))

                #转化图片为单色8
                image_converted = image_result.Convert(PySpin.PixelFormat_Mono8, PySpin.HQ_LINEAR)

                filename = 'Acquisition-%s-%d.jpg' %(device_serial_number, i)

                image_converted.Save(filename)
                print('图像存储为%s' %filename)

                #释放图片，图片直接从相机中检索，为了让其缓存充足，需要释放缓存
                image_result.Release()
                print('')



            except PySpin.SpinnakerException as ex:
                print('Error: %s' %ex)
                result = False

        #结束获取,适当地结束收购有助于确保设备得到清理不需要功率循环来保持完整性。
        cam.EndAcquisition()


    except PySpin.SpinnakerException as ex:
        print('Error: %s' %ex)
        result = False

    return result

def print_device_inof(nodemap_tldevice):
    '''
    打印设备信息,实际这个函数可以不需要
    :param nodemap_tldevice:传输层设备节点映射
    :type:INodeMap
    :return:True代表成功
    '''
    print('***** 设备信息 *****')

    try:
        result = True
        node_device_information = PySpin.CCategoryPtr(nodemap_tldevice.GetNode('DeviceInformation'))
        if PySpin.IsAvailable(node_device_information) and PySpin.IsReadable(node_device_information):
            features = node_device_information.GetFeatures()
            for feature in features:
                node_feature = PySpin.CValuePtr(feature)
                print('%s: %s' % (node_feature.GetName(),
                                  node_feature.ToString() if PySpin.IsReadable(node_feature) else 'Node not readable'))

        else:
            print('Device control information not available.')

    except PySpin.SpinnakerException as ex:
        print('Error: %s' % ex)
        return False


    return result


def run_single_camera(cam):
    '''
    运行单一相机
    :param cam: 再摄像机上运行
    :type:CameraPtr，指向相机对象的指针
    :return: True代表成功，别的情况代表失败
    '''
    try:
        result = True

        #检索TL设备点图同时打印设备信息，这个来自于cameraBase.getTLDeviceNodeMap()
        nodemap_tldevice = cam.GetTLDeviceNodeMap()

        result &= print_device_inof(nodemap_tldevice)

        #初始化相机
        cam.Init()

        #检索相机的节点映射,来自于cameraBase.getNodeMap()，camera继承了cameraBase
        nodemap = cam.GetNodeMap()

        #获取图片
        result &= acquire_images(cam, nodemap, nodemap_tldevice)

        #取消初始化相机
        cam.DeInit()

    except PySpin.SpinnakerException as ex:
        print('Error: %s' %ex)
        result = False

    return False


def main():
    try:
        test_file = open('test.txt', 'w+')
    except IOError:
        print('不能再此文件中写入数据')
        input('Press Enter to exit...')
        return False


    test_file.close()
    os.remove(test_file.name) #删除该文件

    result = True

    #检索系统对象，GetInstance->SystemPtr，SystemPtr是指向系统的跟踪指针
    system = PySpin.System.GetInstance()

    #得到当前版本的
    version = system.GetLibraryVersion()
    print('Library version:%d'%version.major)

    #从系统中检索相机列表GetCameras()->CameraList,CameraList用于保存照相机对象的列表
    cam_list = system.GetCameras()

    #获取连接的相机数目
    num_cameras = cam_list.GetSize()

    #如果没有相机
    if num_cameras == 0:
        #在释放系统前，清空相机列表
        cam_list.Clear()

        #释放系统实例
        system.ReleaseInstance()

        print('没有照相机呀！')
        input('呕吼，按Enter退出吧！')
        return False

    #遍历每个相机,这里cam实际是camera对象了，所以查属性的时候要去查camera，而不是cameraptr
    for i,cam in enumerate(cam_list):
        print('Running example for camera %d...'%i)
        result &= run_single_camera(cam)
        print('Camera %d example complete...\n'%i)
    #使用del优于将变量赋值为None。
    del cam

    #再释放系统前清空相机
    cam_list.Clear()

    #释放系统实例
    system.ReleaseInstance()

    input('完毕，按Enter退出')
    return result

if __name__ == '__main__':
    if main():
        sys.exit(0)
    else:
        sys.exit(1)

